import logs from 'src-ssr/util/logs';
import moment from 'moment';
import { Server } from 'socket.io';
const io = new Server();
import db, { mysql } from 'src-ssr/db/connect';
import wx_msg from 'src-ssr/util/wechat_template_message';
import { URL } from 'node:url';
import * as tools from 'src-ssr/util/common';
const socketPool = {}; // 客户端连接池, { tid: [], ... }
import { portIsOccupied } from 'src-ssr/util/common';
import { Sequelize, Op } from 'sequelize';
import models from 'src-ssr/db/sequelize';
// 取用户数
function uniqueUser (roomName) {
  const uArr = [...new Set(socketPool[roomName].map(v => v.user.id))];

  return uArr.length;
}

// 初始化
const init = async (wsPort, https) => {
  if (process.env.NODE_ENV === 'development') {
    const flag = await portIsOccupied(wsPort);
    if (flag) return;
  }

  io.attach(wsPort);
  if (https) io.attach(https);
  console.info(moment().format('YYYY/MM/DD HH:mm:ss'), 'The websocket has started!');
  io.on('connection', socket => {
    /* 用户登录，把客户端信息输入连接池
      data = {
      tid: 租户id
      id: 用户id,
      nickname: 用户昵称,
      browser: 浏览器,
      os: 操作系统
    } */
    socket.on('login', data => {
      const roomName = `room_${data.tid}`; // 租户房间
      if (!socketPool[roomName]) socketPool[roomName] = []; // 初始化租户连接池
      socket.join(roomName); // 加入租户房间

      socketPool[roomName].push({
        id: socket.id,
        user: data
      });
      socket.user = data;

      // 登录日志
      const ip = socket.request.socket.remoteAddress.match(/\d+\.\d+\.\d+\.\d+/) || [''];
      const xForwardedFor = socket.handshake.headers['x-forwarded-for'];
      logs.recordLogs({
        tid: data.tid,
        id: data.id,
        nickname: data.nickname
      }, { type: 'login' }, {
        ip: xForwardedFor || ip,
        browser: data.browser,
        os: data.os
      });

      // 向除去建立该连接的客户端的所有客户端广播上线通知
      // socket.broadcast.emit('online', {
      //   connects: socketPool.length, // 连接数
      //   users: uniqueUser(), // 用户数
      //   user: data
      // });
      // 向该租户的所有客户端广播上线通知
      io.to(roomName).emit('online', {
        connects: socketPool[roomName].length, // 连接数
        users: uniqueUser(roomName), // 用户数
        user: data
      });
    });

    /** 转发客户端消息
   * @param data object {
   * to, 发给谁
   * message 消息内容
   * }
    */
    socket.on('message', data => {
      const { to, message } = data;
      if (to && message) {
        const roomName = `room_${socket.user.tid}`; // 发送端的租户id，消息只能发给同租户内的用户
        const conn = socketPool[roomName].filter(v => v.user.id == data.to);
        for (const client of conn) {
          io.to(client.id).emit('message', {
            from: {
              id: socket.user.id,
              nickname: socket.user.nickname
            },
            message: data.message
          });
        }
      }
    });

    /** 向客户端转发其它客户端发过来的协同消息
   * @param data object {
   * to, // 发给谁
   * message // 协同消息内容
   * }
    */
    socket.on('cooperation', data => {
      let { to, message } = data;
      if (to && message) {
        const roomName = `room_${socket.user.tid}`; // 发送端的租户id，消息只能发给同租户内的用户
        if (to === '*') { // 广播，broadcast是不发给自己的，所以改成 io.sockets.emit
          /* socket.broadcast.emit('cooperation', {
            from: {
              id: socket.user.id,
              nickname: socket.user.nickname
            },
            message
          }); */
          io.to(roomName).emit('cooperation', {
            from: {
              id: socket.user.id,
              nickname: socket.user.nickname
            },
            message
          });
        } else {
          if (typeof to === 'string' || typeof to === 'number') {
            to = [to];
          }

          to = to.map(v => String(v).trim());

          const conn = socketPool[roomName].filter(v => to.includes(String(v.user.id)) || to.includes(v.user.nickname));
          for (const client of conn) {
            io.to(client.id).emit('cooperation', {
              from: {
                id: socket.user.id,
                nickname: socket.user.nickname
              },
              message
            });
          }
        }
      }
    });

    function logout () {
      // 全池查询租户id
      let tid = '';
      for (const [key, value] of Object.entries(socketPool)) {
        if (value.find(v => v.id === socket.id)) {
          tid = key;
        }
      }

      const roomName = `room_${tid}`;
      console.log('logout', tid);
      if (socketPool[roomName] && socketPool[roomName].length > 0) {
        const offlineUser = socketPool[roomName].filter(s => s.id === socket.id);
        if (offlineUser && offlineUser.length > 0) {
          const user = Object.assign({}, offlineUser[0]);
          socketPool[roomName] = socketPool[roomName].filter(s => s.id !== socket.id); // 删除连接池连接

          // socket.broadcast.emit('offline', { // 下线通知
          //   connects: socketPool.length, // 连接数
          //   users: uniqueUser(), // 用户数
          //   user: user.user
          // });
          io.to(roomName).emit('offline', { // 下线通知
            connects: socketPool[roomName].length, // 连接数
            users: uniqueUser(roomName), // 用户数
            user: user.user
          });

          // 登出日志
          const ip = socket.request.socket.remoteAddress.match(/\d+\.\d+\.\d+\.\d+/) || [''];
          const xForwardedFor = socket.handshake.headers['x-forwarded-for'];
          logs.recordLogs({
            tid,
            id: user.user.id,
            nickname: user.user.nickname
          }, { type: 'logout' }, {
            ip: xForwardedFor || ip,
            browser: user.user.browser,
            os: user.user.os
          });
        }
      }
    }

    socket.on('disconnect', (reason) => {
      console.log('disconnect', reason);
      logout();
    });

    socket.on('error', (reason) => {
      console.log('error', reason);
      logout();
    });
  });
};

/**
 * @function 发送指令
 * @param {string array} receiver 多个用户id,用逗号分格,或是数组
 * @param {object} command {message, module}
 *                          message 提示消息内容
 *                          module 可选，点击后前往的模块名称
 * @param {string} title 标题
 * @param {string} sender 发送人
 * @param {object} wx_message 微信消息体 {id, data: {first, keyword1, keyword2, keyword3, remark}, url}
 *                            id: 模板消息的id
 *                            first, keyword1, keyword2, keyword3, remark均为消息体里的关键字段
 * @param {number} tid 租户id 类型 0-系统用户 大于0-租户id
 */
const sendCommand = async (receiver, command, title, sender = -1, wx_message, tid = 0) => {
  let userarr = [];
  if (Object.prototype.toString.call(receiver) === '[object Array]') {
    userarr = receiver;
  } else {
    userarr = String(receiver).split(',');
  }

  const [users] = await mysql.query(`SELECT u.id, w.openid, d.dd_userid
    FROM m_users u
    LEFT JOIN m_wechat_user w ON u.id=w.user_id
    WHERE u.id IN (?) AND u.tenant_id = ?`, [userarr, tid]);

  const moduleUrl = new URL(command.module, global.domainHost); // 模块链接
  const wx = await tools.getSystemConfig('wechat'); // 微信配置
  // const dd = await tools.getSystemConfig('dd'); // 钉钉配置
  let senderName = '';
  if (sender > 0) {
    senderName = await db.select('m_users', ['nickname'], { id: sender, tenant_id: tid }, true);
    senderName = senderName.nickname;
  }

  const roomName = `room_${tid}`; // 租户房间
  for (const uid of userarr) {
    const conn = socketPool[roomName].filter(v => v.user.id == uid);
    // 发送前端即时消息
    if (conn.length > 0) {
      for (const client of conn) {
        io.to(client.id).emit('command', {
          from: {
            id: sender,
            nickname: senderName
          },
          command: {
            title: title || '系统消息',
            ...command
          }
        });
      }
    }

    // 记录消息发送日志
    const log = {
      title: title || '',
      message: '',
      module: '',
      msg_type: 'user'
    };
    if (!command.message) {
      log.message = JSON.stringify(command);
      log.msg_type = 'system';
    } else {
      log.message = command.message;
    }
    if (command.module) {
      log.module = command.module;
    }

    const logObj = await models.t_message_logs.create({
      create_time: Date.now() / 1000,
      sender,
      receiver: userarr.map(v => Number(v)),
      ...log,
      tenant_id: tid
    });

    const userinfo = users.filter(u => u.id == uid);
    // 发送微信消息
    if (wx.appid && wx.secret && wx.access_token && wx_message && typeof wx_message === 'object' && userinfo[0].openid) {
      wx_msg.sendTemplate(userinfo[0].openid, wx_message, moduleUrl.href).then(res => { // 记录微信返回结果
        logObj.wx_return = JSON.stringify(res);
        logObj.save();
      });
    }

    // 发送钉钉消息
    // if (dd.CorpId && dd.AppKey && dd.AppSecret && title && command.message && userinfo[0].dd_userid) {
    //   dingtalk.sendSingleCard(title, command.message, moduleUrl, userinfo[0].dd_userid, senderName).then(res => { // 记录钉钉返回结果
    //     db.update('message_logs', { dd_return: JSON.stringify(res) }, { id: ret[0].insertId });
    //   });
    // }
  }
};

/**
 * 广播
 * @param {*} type 类型
 * @param {*} message 消息体
 * @param {*} tid 租户
 */
const broadcast = async (type, message, tid = 0) => {
  const roomName = `room_${tid}`; // 租户房间

  // 向该租户的所有客户端广播上线通知
  try {
    io.to(roomName).emit('broadcast', { type, message });
  } catch (e) {
    console.error(e)
  }
};

export default { init, sendCommand, broadcast };
